﻿using Dycox.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dycox.Workflows
{
    public class WorkflowDataAccess : IDisposable
    {

        DataProvider _provider;
        ComponentQueryArguments _arguments;

        public ComponentQueryArguments Arguments
        {
            get { return _arguments; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                if (_arguments != value)
                {
                    if (_provider == null || _provider.DataSource != value.DataSource)
                    {
                        if (_provider != null) _provider.Dispose();
                        _provider = value.CreateProvider();
                    }

                    _arguments = value;
                }
            }
        }

        public WorkflowDataAccess(ComponentQueryArguments args)
        {
            if (args == null)
                throw new ArgumentNullException("args");

            this.Arguments = args;
        }

        public StageAggregatingResult[] CountSteps()
        {
            ThrowIfDisposed();
            
            var q = WFStep.ParseQuery(_provider.WFSteps, _arguments);
            
            return q.GroupBy(o => o.Thread.Process.WorkflowName + Stage.FullNameSeparator + o.StageName).Select(o => new StageAggregatingResult(o.Key, o.Count())).ToArray();
        }

        public WFStep[] QuerySteps(PagingInfo pagingInfo)
        {
            return QuerySteps().Page(pagingInfo).ToArray();
        }

        private IQueryable<WFStep> QuerySteps()
        {
            return WFStep.ParseQuery(_provider.WFSteps, _arguments);
        }

        public StepInfo[] GetSteps()
        {
            return QuerySteps().Select(o => new StepInfo(o.Thread.Process.WorkflowName, o)).ToArray();
        }

        public void Dispose()
        {
            if (_provider != null)
            {
                _provider.Dispose();
                _provider = null;
            }
        }

        private void ThrowIfDisposed()
        {
            if (_provider == null)
                throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    public class StageAggregatingResult
    {
        public StageAggregatingResult(string stageFullName, int count)
        {
            _stage = WorkflowManager.GetStage(stageFullName);
            _stepCount = count;
        }

        private Stage _stage;

        public Stage Stage
        {
            get { return _stage; }
        }

        private int _stepCount;

        public int StepCount
        {
            get { return _stepCount; }
        }

    }

    public class StepInfo
    {
        internal StepInfo(string workflowName, WFStep step)
        {
            _step = step;
            _stage = WorkflowManager.GetWorkflow(workflowName).Stages[step];
        }

        private WFStep _step;

        public WFStep Step
        {
            get { return _step; }
        }

        private Stage _stage;

        public Stage Stage
        {
            get { return _stage; }
        }

        public bool IsActive
        {
            get { return _step.IsActive; }
        }

        public bool IsFinished
        {
            get { return FlowControl.IsComponentFinishedCore(_step); }
        }

        public bool IsCompleted
        {
            get { return _step.Status == WorkflowComponentStatus.Completed; }
        }

        /// <summary>
        /// 获取表示此步骤执行的时长的<see cref="TimeSpan"/>，如果步骤还没有完成、已经中止或没有处于活动状态，返回<see cref="TimeSpan.Zero"/>。
        /// </summary>
        /// <exception cref="WorkflowException">如果完成时间为空引发此异常。</exception>
        public TimeSpan Duration
        {
            get
            {
                if (IsCompleted && !IsActive)
                {
                    if (_step.FinishTime == null)
                        throw new WorkflowException(SR.GetString("should_has_finishing_time"));

                    TimeSpan duration;
                    duration = _step.FinishTime.Value - (_step.StartTime ?? _step.CreationTime);

                    return duration;
                }
                else
                    return TimeSpan.Zero;
            }
        }

        /// <summary>
        /// 获取此步骤的持续时长。
        /// </summary>
        public TimeSpan Elapsed
        {
            get
            {

                if (_step.StartTime.HasValue)
                {
                    DateTime t1 = _step.FinishTime ?? WorkflowManager.TimeService.Now;
                    return t1 - _step.StartTime.Value;
                }
                else
                    return TimeSpan.Zero;
            }
        }

        private StageOperationContract _contract;

        public StageOperationContract Contract
        {
            get
            {
                if (_contract == null)
                    _contract = _stage.GetContract(_step);
                return _contract;
            }
        }

    }
}
